using System;

namespace PGMapper
{
    /// <summary>
    /// Defines an ID property that uniquely identifies this object among other objects of the 
    /// same type.
    /// </summary>
    public interface IReference
    {
        int ID { get; set; }
    }

    /// <summary>
    /// Represents a foreign key reference to another object (table) in the database, specified
    /// and tracked by either the object itself or just the object's ID.
    /// </summary>
    public class Reference<T> : IReference
        where T : Mapped<T>, IReference
    {
        public int ID { get; set; }

        public Reference() { }

        public Reference(T referenced)
        {
            if (referenced != null)
                ID = referenced.ID;
        }

        public Reference(int id)
        {
            this.ID = id;
        }

        /// <summary>
        /// Looks up the object we're referencing from the database.
        /// </summary>
        public T Find()
        {
            return Mapped<T>.Find(ID);
        }

        /// <summary>
        /// Allows you to assign instances of T directly to a Reference property.
        /// </summary>
        public static implicit operator Reference<T>(T value)
        {
            return new Reference<T>(value);
        }

        public override string ToString()
        {
            return string.Format("<Reference to {0}:{1}>", typeof(T).Name, ID);
        }
    }

    static class ReferenceExtensions
    {
        public static IReference CreateReference(this Type type, int id)
        {
            var reference = (IReference)Activator.CreateInstance(type);
            reference.ID = id;
            return reference;
        }

        public static bool IsReference(this Type type)
        {
            return type.IsGenericType &&
                typeof(Reference<>).IsAssignableFrom(type.GetGenericTypeDefinition());
        }

        public static Type GetReferencedType(this Type type)
        {
            return type.GetGenericArguments()[0];
        }
    }
}
